<?php
namespace PhpComposer\simhashCore;

class SimHash
{
    private int $hashBits = 64;
    
    /**
     * 计算文本的SimHash值
     */
    public function calculate(string $text): int
    {
        $processedText = TextProcessor::preprocess($text);
        $tokens = TextProcessor::tokenize($processedText);
        $ngrams = TextProcessor::generateNgrams($tokens);
        
        // 合并关键词和n-gram特征
        $features = array_merge($tokens, $ngrams);
        
        // 计算特征权重
        $weights = $this->calculateWeights($features, $tokens);
        
        // 计算哈希向量
        $vector = array_fill(0, $this->hashBits, 0);
        foreach ($features as $feature) {
            $hash = $this->hashFeature($feature);
            $weight = $weights[$feature] ?? 1;
            
            for ($i = 0; $i < $this->hashBits; $i++) {
                $mask = 1 << $i;
                if ($hash & $mask) {
                    $vector[$i] += $weight;
                } else {
                    $vector[$i] -= $weight;
                }
            }
        }
        
        // 生成最终哈希值
        $simhash = 0;
        for ($i = 0; $i < $this->hashBits; $i++) {
            if ($vector[$i] > 0) {
                $simhash |= 1 << $i;
            }
        }
        
        return $simhash;
    }
    
    /**
     * 计算特征权重
     */
    private function calculateWeights(array $features, array $tokens): array
    {
        $weights = [];
        $tokenCounts = array_count_values($tokens);
        $total = count($tokens);
        
        foreach ($features as $feature) {
            // 关键词权重高于n-gram
            if (in_array($feature, $tokens)) {
                $freq = $tokenCounts[$feature] ?? 1;
                $weights[$feature] = (int)(($freq / $total) * 200);
            } else {
                $weights[$feature] = 60; // n-gram权重较低
            }
        }
        
        return $weights;
    }
    
    /**
     * 计算特征的哈希值
     */
    private function hashFeature(string $feature): int
    {
        $hash = hash('sha256', $feature, true);
        return (int)substr(bin2hex($hash), 0, 16);
    }
    
    /**
     * 计算两个SimHash的相似度
     */
    public function getSimilarity(int $hash1, int $hash2): float
    {
        $hammingDistance = substr_count(decbin($hash1 ^ $hash2), '1');
        $similarity = (64 - $hammingDistance) / 64 * 100;
        
        // 相似度修正
        if ($hammingDistance < 15) {
            $similarity = min(100, $similarity + (15 - $hammingDistance) * 0.5);
        }
        
        return round($similarity, 2);
    }
}
    